package com.anji.plus.modules.user.service.impl;

import com.anji.plus.aop.GaeaQuery;
import com.anji.plus.common.MagicValueConstants;
import com.anji.plus.common.RespCommonCode;
import com.anji.plus.gaea.bean.TreeNode;
import com.anji.plus.gaea.cache.CacheHelper;
import com.anji.plus.gaea.constant.BaseOperationEnum;
import com.anji.plus.gaea.constant.Enabled;
import com.anji.plus.gaea.constant.GaeaConstant;
import com.anji.plus.gaea.constant.GaeaKeyConstant;
import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
import com.anji.plus.gaea.exception.BusinessException;
import com.anji.plus.gaea.exception.BusinessExceptionBuilder;
import com.anji.plus.gaea.holder.UserContentHolder;
import com.anji.plus.modules.menu.controller.dto.TreeDTO;
import com.anji.plus.modules.org.dao.GaeaOrgMapper;
import com.anji.plus.modules.org.dao.entity.GaeaOrg;
import com.anji.plus.modules.role.controller.dto.RoleOrgDto;
import com.anji.plus.modules.role.dao.GaeaRoleOrgMapper;
import com.anji.plus.modules.user.controller.dto.GaeaUserQueryDto;
import com.anji.plus.modules.user.controller.param.GaeaUserPasswordParam;
import com.anji.plus.modules.user.controller.param.GaeaUserQueryParam;
import com.anji.plus.modules.user.controller.param.UserRoleOrgReqParam;
import com.anji.plus.modules.user.dao.GaeaUserMapper;
import com.anji.plus.modules.user.dao.GaeaUserRoleOrgMapper;
import com.anji.plus.modules.user.dao.entity.GaeaUser;
import com.anji.plus.modules.user.dao.entity.GaeaUserRoleOrg;
import com.anji.plus.modules.user.service.GaeaUserService;
import com.anji.plus.util.DecryptUtil;
import com.anji.plus.util.MD5Util;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 用户表(GaeaUser)ServiceImpl
 *
 * @author lr
 * @since 2021-02-02 13:38:12
 */
@Service
public class GaeaUserServiceImpl implements GaeaUserService {
    @Autowired
    private GaeaUserMapper gaeaUserMapper;
    @Autowired
    private GaeaUserRoleOrgMapper gaeaUserRoleOrgMapper;
    @Autowired
    private GaeaRoleOrgMapper gaeaRoleOrgMapper;
    @Autowired
    private GaeaOrgMapper gaeaOrgMapper;

    @Autowired
    private CacheHelper cacheHelper;


    @Override
    public GaeaBaseMapper<GaeaUser> getMapper() {
        return gaeaUserMapper;
    }

    @Override
    public GaeaUser getUserByUsername(String username) {
        LambdaQueryWrapper<GaeaUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(GaeaUser::getUsername, username);
        return gaeaUserMapper.selectOne(queryWrapper);
    }

    @Override
    public TreeDTO queryRoleTree(String username) {
        //该用户已经关联的组织角色
        LambdaQueryWrapper<GaeaUserRoleOrg> userRoleOrgWrapper = Wrappers.lambdaQuery();
        userRoleOrgWrapper.select(GaeaUserRoleOrg::getRoleCode, GaeaUserRoleOrg::getOrgCode).eq(GaeaUserRoleOrg::getUsername, username);
        //组装出选中的id
        List<GaeaUserRoleOrg> userRoleOrgList = gaeaUserRoleOrgMapper.selectList(userRoleOrgWrapper);
        List<String> checkedIds = userRoleOrgList.stream()
                .map(userRoleOrgPO -> String.format("%s_%s", userRoleOrgPO.getOrgCode(), userRoleOrgPO.getRoleCode()))
                .collect(Collectors.toList());
        //所有的角色组织
        List<RoleOrgDto> roleOrgDtos = gaeaRoleOrgMapper.queryAllRoleOrg();
        //所有的组织
        LambdaQueryWrapper<GaeaOrg> queryOrgWrapper = Wrappers.lambdaQuery();
        queryOrgWrapper.select(GaeaOrg::getId, GaeaOrg::getOrgCode, GaeaOrg::getOrgName, GaeaOrg::getOrgParentCode)
                .eq(GaeaOrg::getEnabled, Enabled.YES.getValue());
        List<GaeaOrg> orgList = gaeaOrgMapper.selectList(queryOrgWrapper);
        List<TreeNode> tree = buildOrgRoleTree(orgList, roleOrgDtos, "0");
        TreeDTO resultData = new TreeDTO();
        resultData.setTreeDatas(tree);
        resultData.setCheckedCodes(checkedIds);
        return resultData;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveRoleTree(UserRoleOrgReqParam reqParam) {
        String username = reqParam.getUsername();
        List<String> orgRoleCodes = reqParam.getRoleOrgCodes();
        //该用户已经关联的组织角色
        LambdaQueryWrapper<GaeaUserRoleOrg> userRoleOrgWrapper = Wrappers.lambdaQuery();
        userRoleOrgWrapper.select(GaeaUserRoleOrg::getOrgCode, GaeaUserRoleOrg::getRoleCode)
                .eq(GaeaUserRoleOrg::getUsername, username);
        gaeaUserRoleOrgMapper.delete(userRoleOrgWrapper);

        //删除缓存
        String key = GaeaKeyConstant.USER_ROLE_SET_PREFIX + username;
        cacheHelper.delete(key);

        if (CollectionUtils.isEmpty(orgRoleCodes)) {
            return true;
        }
        List<GaeaUserRoleOrg> list = new ArrayList<>();


        orgRoleCodes.stream()
                .filter(codeStr -> StringUtils.isNotBlank(codeStr) && codeStr.contains(GaeaConstant.REDIS_SPLIT))
                .forEach(codesStr -> {
            GaeaUserRoleOrg userRoleOrg = new GaeaUserRoleOrg();
            String[] info = codesStr.split(GaeaConstant.REDIS_SPLIT);
            userRoleOrg.setOrgCode(info[0]);
            userRoleOrg.setRoleCode(info[1]);
            userRoleOrg.setUsername(username);
            list.add(userRoleOrg);
        });
        gaeaUserRoleOrgMapper.insertBatch(list);

        //刷新用户、机构、角色GaeaKeyConstant.USER_ROLE_SET_PREFIX + : + orgCode => map
        Map<String, String> userOrgRoleMap = list.stream()
                .collect(Collectors.groupingBy(GaeaUserRoleOrg::getOrgCode,
                        Collectors.mapping(GaeaUserRoleOrg::getRoleCode, Collectors.joining(GaeaConstant.SPLIT))));


        cacheHelper.hashSet(key, userOrgRoleMap);
        return true;
    }

    @Override
    public Boolean updatePassword(GaeaUserPasswordParam requestParam) {
        //参数校验
        if (!requestParam.getConfirmPassword().equals(requestParam.getPassword())) {
            //密码和确认密码不一致
            throw BusinessExceptionBuilder.build(RespCommonCode.AUTH_PASSWORD_NOTSAME);
        }
        //新密码不能与老密码一样
        if (StringUtils.equals(requestParam.getOldPassword(), requestParam.getPassword())) {
            throw BusinessExceptionBuilder.build(RespCommonCode.USER_PASSWORD_CONFIG_PASSWORD_CANOT_EQUAL);
        }
        String password = DecryptUtil.decrypt(requestParam.getPassword());
        String oldPassword = DecryptUtil.decrypt(requestParam.getOldPassword());
        String username = UserContentHolder.getContext().getUsername();

        LambdaQueryWrapper<GaeaUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.select(GaeaUser::getId, GaeaUser::getPassword)
                .eq(GaeaUser::getUsername, username);
        GaeaUser gaeaUser = gaeaUserMapper.selectOne(queryWrapper);

        if (DecryptUtil.matches(gaeaUser.getPassword(), oldPassword)) {
            throw BusinessExceptionBuilder.build(RespCommonCode.OLD_PASSWORD_ERROR);
        }
        gaeaUser.setPassword(password);
        int flag = gaeaUserMapper.updateById(gaeaUser);
        //返回结果
        return flag > 0;
    }


    @Override
    public List<GaeaOrg> getOrgByUsername(String username) {
        return gaeaUserRoleOrgMapper.getOrgInfoByUsername(username);
    }

    @Override
    public List<String> getRoleByUserOrg(String username, String orgCode) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaUserRoleOrg::getRoleCode,GaeaUserRoleOrg::getId)
                .eq(GaeaUserRoleOrg::getUsername,username)
                .and(StringUtils.isNotBlank(orgCode),e->e.eq(GaeaUserRoleOrg::getOrgCode, orgCode));
        List<GaeaUserRoleOrg> gaeaUserRoleOrgList = gaeaUserRoleOrgMapper.selectList(wrapper);
        return gaeaUserRoleOrgList.stream().map(GaeaUserRoleOrg::getRoleCode).collect(Collectors.toList());
    }

    @Override
    public Boolean setDefaultPwd(String username) {
        GaeaUser user = this.getUserByUsername(username);
        String md5Pwd = MD5Util.encryptBySalt(MagicValueConstants.DEFAULT_PASSWORD);
        String defaultPwd = DecryptUtil.decrypt(md5Pwd);
        user.setPassword(defaultPwd);
        int flag = gaeaUserMapper.updateById(user);
        return flag > 0;
    }

    /**
     * 组织角色树
     *
     * @param orgList
     * @param pid
     * @return
     */
    private static List<TreeNode> buildOrgRoleTree(List<GaeaOrg> orgList, List<RoleOrgDto> roleOrgVOS, Object pid) {
        List<TreeNode> childList = new ArrayList<>();
        orgList.forEach(orgPO -> {
            if (StringUtils.isNotEmpty(orgPO.getOrgParentCode()) && orgPO.getOrgParentCode().equals(pid)) {
                TreeNode treeVO = new TreeNode();
                treeVO.setId(orgPO.getOrgCode());
                treeVO.setLabel(orgPO.getOrgName());
                childList.add(treeVO);
            }
        });
        childList.forEach(treeVO -> {
            List<TreeNode> treeList = buildOrgRoleTree(orgList, roleOrgVOS, treeVO.getId());
            List<TreeNode> collect = roleOrgVOS.stream().filter(roleOrgVO -> roleOrgVO.getOrgCode().equals(treeVO.getId()))
                    .map(roleOrgVO -> {
                        TreeNode treeEntity = new TreeNode();
                        String roleOrgTreeId = String.format("%s_%s", treeVO.getId(), roleOrgVO.getRoleCode());
                        treeEntity.setId(roleOrgTreeId);
                        treeEntity.setLabel(roleOrgVO.getRoleName());
                        return treeEntity;
                    }).collect(Collectors.toList());
            if (!treeList.isEmpty()) {
                collect.addAll(treeList);
            }
            treeVO.setChildren(collect);
        });
        return childList;
    }

    /**
     * 新增用户，需要设置默认密码
     * @param entity        前端传递的对象
     * @param operationEnum 操作类型
     * @throws BusinessException
     */
    @Override
    public void processBeforeOperation(GaeaUser entity, BaseOperationEnum operationEnum) throws BusinessException {
        switch (operationEnum) {
            case INSERT:
                setDefaultPwd(entity);
                break;
            default:
        }
    }

    private void setDefaultPwd(GaeaUser entity) {
        String md5Pwd = MD5Util.encryptBySalt(MagicValueConstants.DEFAULT_PASSWORD);
        String defaultPwd = DecryptUtil.decrypt(md5Pwd);
        entity.setPassword(defaultPwd);
    }

    @Override
    public void refreshCache() {
        List<GaeaUser> gaeaUsers = gaeaUserMapper.selectList(Wrappers.emptyWrapper());

        Map<String, String> map = gaeaUsers.stream().collect(Collectors.toMap(GaeaUser::getUsername, GaeaUser::getNickname, (v1, v2) -> v2));

        String key = GaeaKeyConstant.USER_NICKNAME_KEY;
        cacheHelper.delete(key);
        cacheHelper.hashSet(key, map);
    }

    @Override
    public List<String> getOrgCodes(String username) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.select(GaeaUserRoleOrg::getOrgCode);
        wrapper.eq(GaeaUserRoleOrg::getUsername, username);
        return gaeaUserRoleOrgMapper.selectList(wrapper).stream()
                .map(GaeaUserRoleOrg::getOrgCode).distinct()
                .collect(Collectors.toList());
    }


    @Override
    @GaeaQuery
    public Page<GaeaUserQueryDto> queryAdvanceUserInfo(GaeaUserQueryParam param, QueryWrapper... wrappers) {
        Page<GaeaUserQueryDto> page = new Page<>(param.getPageNumber(), param.getPageSize());
        QueryWrapper queryWrapper = (null != wrappers && wrappers.length > 0) ? wrappers[0] : null;
        List<GaeaUserQueryDto> list= gaeaUserMapper.queryUserAdvance(page,param,queryWrapper);
        page.setRecords(list);
        return page;
    }

    @Override
    public List<String> getOrgRoleMappings(String username) {
        LambdaQueryWrapper<GaeaUserRoleOrg> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaUserRoleOrg::getUsername, username);
        return gaeaUserRoleOrgMapper.selectList(wrapper)
                .stream()
                .map(gaeaUserRoleOrg -> gaeaUserRoleOrg.getOrgCode() + GaeaConstant.REDIS_SPLIT + gaeaUserRoleOrg.getRoleCode())
                .collect(Collectors.toList());
    }
}
